#!/usr/bin/env bash

#####################################################################
# File Name: install_jenkins.sh
# Version: V1.0
# Author: Brian Hsiung
# Created Time : 2025-02-14 17:09:23
# Description: Jenkins部署脚本（2.492.2）
####################################################################

# set -ex

# root  g6Mmn@1PxY!BFkrsGkU7ik

declare -r JENKINS_PORT="8088"
declare -r JENKINS_USER="jenkins"
declare -r JENKINS_JVM="2048m"
declare -r JENKINS_HOME="/opt/jenkins"
declare -r JENKINS_TMP_DIR="$JENKINS_HOME/tmp"
declare -r JDK_HOME="/usr/local/java/jdk21"
declare -r OSS_BASE_URL="https://brianhsiung.oss-cn-hangzhou.aliyuncs.com"

WORK_DIR="$(cd "$(dirname "$0")" && pwd)"
cd "$WORK_DIR" || exit

### 日志函数
log_info()    { echo -e "\033[34m[INFO] $*\033[0m"; }
log_success() { echo -e "\033[32m[SUCCESS] $*\033[0m"; }
log_warn()    { echo -e "\033[33m[WARNING] $*\033[0m"; }
log_error()   { echo -e "\033[31m[ERROR] $*\033[0m"; exit 1; }

### 检查依赖
check_dependency() {
    local -a required_cmds=("wget" "tar" "groupadd" "useradd") 
    local missing=()

    for cmd in "${required_cmds[@]}"; do
        if ! command -v "$cmd" &>/dev/null; then
            missing+=("$cmd")
        fi
    done

    ((${#missing[@]})) && log_error "缺失依赖命令: ${missing[*]}"
}
### 初始化环境
init_env () {

    [ "$(id -u)" -ne 0 ] && log_error "请使用root账号执行此脚本！"

    if ss -tuln | grep -q ":$JENKINS_PORT " >/dev/null; then
        log_error "端口 $JENKINS_PORT 被占用"
    fi

    if ! getent group "$JENKINS_USER" >/dev/null 2>&1; then
        log_info "创建用户组 $JENKINS_USER"
        groupadd "$JENKINS_USER"
    fi

    if ! getent passwd "$JENKINS_USER" >/dev/null 2>&1; then
        log_info "创建用户 $JENKINS_USER"
        # 需要登录，所以需要创建home目录
        useradd -g "$JENKINS_USER" "$JENKINS_USER"
    fi

    if ! rpm -qa | grep fontconfig >/dev/null 2>&1 ;then
        log_error "缺少字体包，请先安装fontconfig字体包！"
    fi

    if systemctl is-active firewalld &>/dev/null; then
        log_info "开放端口 $JENKINS_PORT"
        firewall-cmd --zone=public --add-port=$JENKINS_PORT/tcp --permanent
        firewall-cmd --reload
    fi

    if [ ! -d "$JENKINS_HOME" ]; then
        log_info "创建目录 $JENKINS_HOME"
        mkdir -p "$JENKINS_HOME" || log_error "无法创建目录 $JENKINS_HOME"
        mkdir -p "$JENKINS_TMP_DIR" || log_error "无法创建目录 $JENKINS_TMP_DIR"
    elif [ "$(ls -A "$JENKINS_HOME")" ]; then
        log_error "目录 $JENKINS_HOME 不为空，请先处理"
    fi

}

#############################################
# 设置目录权限（递归检查上级目录）
#############################################
set_parent_permissions() {
    ### 某些等保三服务器默认新建的的目录权限为700，文件权限为600，导致服务无法启动，所以需要遍历上级目录，将权限修改为755
    # 获取真实目录
    local target_dir
    target_dir=$(realpath "$1")
    # 获取上级目录
    local current_dir
    current_dir=$(dirname "$target_dir")

    # 做个判断，避免进入死循环
    [ -d "$target_dir" ] || log_error "目录不存在 [$target_dir]"

    # 排除敏感目录
    local -a exclude_dirs
    exclude_dirs=("/usr" "/usr/local" "/bin" "/sbin" "/lib" "/etc" "/dev" "/var" "/root" "/usr/share")

    while [[ "$current_dir" != "/" ]]; do

        # 排除敏感目录
        if printf '%s\n' "${exclude_dirs[@]}" | grep -qx "$current_dir"; then
            log_info "跳过系统保护目录: $current_dir"
            current_dir=$(dirname "$current_dir")
            continue
        fi

        # 跳过不存在的目录
        if [[ ! -d "$current_dir" ]]; then
            log_warn "警告: 目录不存在 [$current_dir]"
            current_dir=$(dirname "$current_dir")
            continue
        fi
        
        # 获取当前目录权限
        local perm_oct
        perm_oct=$(stat -c '%a' "$current_dir")
        log_info "目录 $current_dir 的原始权限为: $perm_oct"
        
        if [[ "$perm_oct" != "755" ]]; then
            log_info "正在设置权限: [755] -> $current_dir"
            if ! chmod 755 "$current_dir"; then
                log_error "错误: 无法设置目录权限 [$current_dir]"
            fi
        else
            log_success "权限正确 [755]: $current_dir"
        fi

        # 向上一级目录
        current_dir=$(dirname "$current_dir")
    done
}

#------------------------------------- Install JDK21U -------------------------------------#
install_jdk() {

    log_info "安装JDK21"

    local arch

    arch="$(uname -m)"

    case "$arch" in
        aarch64|arm64)
            arch_type=aarch64 ;;
        amd64|x86_64)
            arch_type=x64 ;;
        *)
            log_error "不支持的架构: $arch" ;;
    esac

    local jdk_pkg="OpenJDK21U-jdk_${arch_type}_linux_hotspot_21.0.6_7.tar.gz"
    
    if [ "$access_mode" = 'external' ] && [ ! -f "$jdk_pkg" ]; then
        log_info "通过 $OSS_BASE_URL/deploy/jdk/$jdk_pkg 下载安装包"
        wget -q "$OSS_BASE_URL/deploy/jdk/$jdk_pkg" || log_error "下载jdk失败"
    fi

    [ -f "$jdk_pkg" ] || log_error "此目录下无 $jdk_pkg 安装包，请检查"

    if [ ! -d "$JDK_HOME" ]; then
        mkdir -p "$JDK_HOME"
        tar -zxf "$jdk_pkg" -C "$JDK_HOME" --strip-components=1 --no-same-owner
        find "$JDK_HOME/bin" -type f -exec chmod 755 {} \;
    elif [ ! "$(ls -A "$JDK_HOME")" ]; then
        log_info "目录存在且为空"
        tar -zxf "$jdk_pkg" -C "$JDK_HOME" --strip-components=1 --no-same-owner
        find "$JDK_HOME/bin" -type f -exec chmod 755 {} \;
    fi

    if [ ! -f "$JDK_HOME/bin/java" ] || [ ! -x "$JDK_HOME/bin/java" ]; then
        log_error "Java可执行文件不存在或无执行权限，JDK安装失败"
    fi

    # 某些服务器上面默认新建的的目录权限为700，将目录权限修改为755
    log_info "设置目录权限"
    chmod 755 "$JDK_HOME"
    set_parent_permissions "$JDK_HOME"

    log_success "JDK安装成功"
    "$JDK_HOME/bin/java" -version

}

#------------------------------------- Install Jenkins -------------------------------------#
install_jenkins() {

    if [  "$access_mode" = 'external' ] && [ ! -f jenkins.war ]; then
        log_info "在线下载jenkins.war"
        wget -q "$OSS_BASE_URL/deploy/jenkins/jenkins.war" || log_error "下载jenkins失败"
    fi

    [ ! -f jenkins.war ] && log_error "当前目录下无jenkins.war，请检查！"

    chmod 755 $JENKINS_HOME
    set_parent_permissions "$JENKINS_HOME"
    /bin/cp -f jenkins.war $JENKINS_HOME
    chown -R $JENKINS_USER:$JENKINS_USER $JENKINS_HOME

# -Dhudson.model.DirectoryBrowserSupport.CSP="script-src 'unsafe-inline'" \\
# 为CSP配置，允许直接在 HTML 中嵌入 JavaScript，这可能会增加 XSS 的风险
# 理想状态为：-Dhudson.model.DirectoryBrowserSupport.CSP="script-src 'self' https://example.com;" 后面跟可信任的地址
# -Djenkins.install.runSetupWizard=false (跳过安装向导)
    cat > /usr/lib/systemd/system/jenkins.service <<EOF
[Unit]
Description=Jenkins Continuous Integration Server
Documentation=https://www.jenkins.io/doc/
Requires=network.target
After=network.target

[Service]
Type=simple

User=$JENKINS_USER
Group=$JENKINS_USER

Environment="JENKINS_HOME=$JENKINS_HOME"
Environment="JENKINS_PORT=$JENKINS_PORT"
ExecStart=$JDK_HOME/bin/java -XX:+UseG1GC \\
            -Xmx$JENKINS_JVM -Xms$JENKINS_JVM \\
            -Duser.timezone=Asia/Shanghai \\
            -Djava.awt.headless=true \\
            -Djava.io.tmpdir=$JENKINS_TMP_DIR \\
            -jar $JENKINS_HOME/jenkins.war --httpPort=$JENKINS_PORT --logfile=$JENKINS_HOME/jenkins.log

Restart=on-failure

LimitNOFILE=65536

[Install]
WantedBy=multi-user.target

EOF
    systemctl daemon-reload
    systemctl enable --now jenkins

    # 等待安装完成
    local timeout=150
    while (( timeout > 0 )); do
        echo "等待Jenkins安装完成..."
        if [ -f "$JENKINS_HOME/secrets/initialAdminPassword" ]; then
            log_success "Jenkins启动成功，初始密码: $(cat $JENKINS_HOME/secrets/initialAdminPassword)"
            break
        fi
        sleep 30
        (( timeout -= 30))
    done

    (( timeout <= 0 )) && log_error "Jenkins启动超时"
    
}

### 安装插件
install_plugins() {

    log_info "处理插件, 停止Jenkins"
    systemctl stop jenkins

    if [  "$access_mode" = 'external' ] && [ ! -f plugins.tar.gz ];then
        log_info "通过 $OSS_BASE_URL/deploy/jenkins/plugins.tar.gz 下载插件"
        wget -q "$OSS_BASE_URL/deploy/jenkins/plugins.tar.gz" || log_error "plugins.tar.gz下载失败"
    fi

    [ ! -f plugins.tar.gz ] && log_error "当前目录下无plugins.tar.gz，请检查！"

    # 安装插件
    if [ -d "$JENKINS_HOME/plugins" ]; then
        if [ "$(ls -A $JENKINS_HOME/plugins)" ]; then
            log_info "删除原有插件"
            rm -rf "$JENKINS_HOME/plugins/*"
        fi
        log_info "解压插件至$JENKINS_HOME/plugins"
        tar -zxf plugins.tar.gz -C $JENKINS_HOME/plugins --strip-components=1 --no-same-owner
        chown -R $JENKINS_USER:$JENKINS_USER $JENKINS_HOME/plugins
    else
        log_info "解压插件至$JENKINS_HOME/plugins"
        mkdir -p "$JENKINS_HOME"/plugins
        tar -zxf plugins.tar.gz -C $JENKINS_HOME/plugins --strip-components=1 --no-same-owner
        chown -R $JENKINS_USER:$JENKINS_USER $JENKINS_HOME/plugins
    fi

    # 重启Jenkins
    log_info "重启Jenkins"
    systemctl restart jenkins
}


main() {

    log_info "开始安装Jenkins"

    if curl --output /dev/null --silent --head --fail --connect-timeout 5 --max-time 10 "$OSS_BASE_URL"/health; then
        log_info "使用在线安装模式"
        access_mode='external'
    fi

    check_dependency

    init_env
    if [ -d "$JDK_HOME" ] && [ -f "$JDK_HOME/bin/java" ]; then
        log_info "JDK已安装，跳过安装步骤, 版本如下："
        "$JDK_HOME/bin/java" -version
    else
        install_jdk
    fi
    install_jenkins
    install_plugins

    log_success "Jenkins安装完成"

    log_info "访问地址: http://$(hostname -I | awk '{print $1}'):$JENKINS_PORT"
    log_info "初始密码: $(cat $JENKINS_HOME/secrets/initialAdminPassword)"
    log_info "Jenkins日志: $JENKINS_HOME/jenkins.log"
}

main